package org.daochong.lang.security;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.crypto.Cipher;

import org.daochong.lang.BeanUtil;
import org.daochong.lang.StringUtils;

import sun.misc.BASE64Encoder;
import sun.security.x509.AccessDescription;
import sun.security.x509.AlgorithmId;
import sun.security.x509.AuthorityInfoAccessExtension;
import sun.security.x509.AuthorityKeyIdentifierExtension;
import sun.security.x509.CRLDistributionPointsExtension;
import sun.security.x509.CertAndKeyGen;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.DistributionPoint;
import sun.security.x509.FreshestCRLExtension;
import sun.security.x509.GeneralName;
import sun.security.x509.GeneralNameInterface;
import sun.security.x509.GeneralNames;
import sun.security.x509.KeyIdentifier;
import sun.security.x509.OIDMap;
import sun.security.x509.URIName;
import sun.security.x509.X500Name;
import sun.security.x509.X509CRLImpl;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

@SuppressWarnings("all")
class CertificateResult {
	private Certificate certificate;
	private X509CertInfo certInfo;
	private PrivateKey privateKey;
	private PublicKey publicKey;
	private CertificateRequest request;
	private Certificate[] certificateChain;

	public Certificate[] getCertificateChain() {
		return certificateChain;
	}

	public void setCertificateChain(Certificate[] certificateChain) {
		this.certificateChain = certificateChain;
	}

	public Certificate getCertificate() {
		return certificate;
	}

	public X509CertInfo getCertInfo() {
		return certInfo;
	}

	public PrivateKey getPrivateKey() {
		return privateKey;
	}

	public PublicKey getPublicKey() {
		return publicKey;
	}

	public CertificateRequest getRequest() {
		return request;
	}

	public void setCertificate(Certificate certificate) {
		this.certificate = certificate;
	}

	public void setCertInfo(X509CertInfo certInfo) {
		this.certInfo = certInfo;
	}

	public void setPrivateKey(PrivateKey privateKey) {
		this.privateKey = privateKey;
	}

	public void setPublicKey(PublicKey publicKey) {
		this.publicKey = publicKey;
	}

	public void setRequest(CertificateRequest request) {
		this.request = request;
	}
}

@SuppressWarnings("all")
public class DaoChongCertificate {

	public static int CACHE_LENGTH = 1024;

	@SuppressWarnings("unchecked")
	private static void check(List<DistributionPoint> list, X509Certificate c) {
		try {
			for (DistributionPoint point : list) {
				GeneralNames gns = point.getCRLIssuer();
				if (gns != null) {
					for (GeneralName gn : gns.names()) {
						GeneralNameInterface gni = gn.getName();
						if (gni instanceof URIName) {
							URIName name = (URIName) gni;
							InputStream in = name.getURI().toURL().openStream();
							X509CRLImpl crls = X509CRLImpl.toImpl(DaoChongCRL.loadCRLS(in));
							if (crls.getRevokedCertificate(c.getSerialNumber()) != null) {
								throw new RuntimeException("Certificate is Revoke");
							}
							FreshestCRLExtension cs = (FreshestCRLExtension) crls
									.getExtension(OIDMap.getOID(CertificateRequest.OID_FreshestCRL));
							if (cs != null) {
								List<DistributionPoint> newList = (List<DistributionPoint>) cs
										.get(CRLDistributionPointsExtension.POINTS);
								check(newList, c);
							}
						}
					}
				}
			}

		} catch (RuntimeException e) {
			throw e;
		} catch (Throwable e) {
			throw new RuntimeException("Check Certificate Fail", e);
		}
	}

	@SuppressWarnings("unchecked")
	public static void check(X509Certificate c) {
		try {
			c.checkValidity();
			X509CertImpl cert = X509CertImpl.toImpl(c);
			AuthorityInfoAccessExtension auths = cert.getAuthorityInfoAccessExtension();
			if (auths != null) {
				for (AccessDescription ad : auths.getAccessDescriptions()) {
					GeneralNameInterface gn = ad.getAccessLocation().getName();
					if (gn instanceof URIName) {
						URIName name = (URIName) gn;
						InputStream in = name.getURI().toURL().openStream();
						X509Certificate root = loadCert(in);
						if (!root.getSubjectDN().equals(c.getIssuerDN())) {
							throw new RuntimeException("The Certificate it not ISSUE by '" + name.getURI() + "'");
						}
					}
				}
			}
			CRLDistributionPointsExtension de = cert.getCRLDistributionPointsExtension();
			if (de != null) {
				List<DistributionPoint> list = (List<DistributionPoint>) de.get(CRLDistributionPointsExtension.POINTS);
				check(list, c);
			}

		} catch (RuntimeException e) {
			throw e;
		} catch (Throwable e) {
			throw new RuntimeException("Check Certificate Fail", e);
		}
	}

	public static void createCrts(OutputStream out, Certificate... certs) {
		try {
			byte[] start = "-----BEGIN CERTIFICATE-----\n".getBytes();
			byte[] end = "-----END CERTIFICATE-----".getBytes();
			for (int i = 0; i < certs.length; i++) {
				Certificate cert = certs[i];
				out.write(start);
				out.write(cert.getEncoded());
				out.write(end);
				if (i + 1 < certs.length) {
					out.write('\n');
				}
			}
		} catch (Throwable e) {
			throw new RuntimeException("UnionCertificates Fail", e);
		}
	}

	public static void createCrts(OutputStream out, List<Certificate> certs) {
		try {
			byte[] start = "-----BEGIN CERTIFICATE-----\n".getBytes();
			byte[] end = "-----END CERTIFICATE-----".getBytes();
			for (int i = 0; i < certs.size(); i++) {
				Certificate cert = certs.get(i);
				out.write(start);
				out.write(cert.getEncoded());
				out.write(end);
				if (i + 1 < certs.size()) {
					out.write('\n');
				}
			}
		} catch (Throwable e) {
			throw new RuntimeException("UnionCertificates Fail", e);
		}
	}

	public static byte[] decrypt(Key key, byte[] data) {
		try {
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, key);
			int keySize = ((RSAKey) key).getModulus().toString(2).length();
			int blockSize = keySize / 8;
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;
			while (data.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(data, j * blockSize, blockSize));
				j++;
			}
			byte[] re = bout.toByteArray();
			bout.close();
			return re;
		} catch (Exception e) {
			throw new RuntimeException("decrypt fail", e);
		}
	}

	public static void decrypt(Key key, InputStream in, OutputStream out) {
		try {
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, key);
			int keySize = ((RSAKey) key).getModulus().toString(2).length();
			int blockSize = keySize / 8;
			byte[] data = new byte[blockSize];
			int len = -1;
			while ((len = in.read(data)) != -1) {
				out.write(cipher.doFinal(data, 0, len));
			}
			out.close();
			in.close();
		} catch (Exception e) {
			throw new RuntimeException("decrypt fail", e);
		}
	}

	public static byte[] encrypt(Key key, byte[] data) {
		try {
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, key);
			int inputLen = data.length;
			int keySize = ((RSAKey) key).getModulus().toString(2).length();
			int blockSize = keySize / 8 - 11;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > blockSize) {
					cache = cipher.doFinal(data, offSet, blockSize);
				} else {
					cache = cipher.doFinal(data, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * blockSize;
			}
			byte[] re = out.toByteArray();
			out.close();
			return re;
		} catch (Exception e) {
			throw new RuntimeException("encrypt fail", e);
		}
	}

	public static void encrypt(Key key, InputStream in, OutputStream out) {
		try {
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, key);
			int keySize = ((RSAKey) key).getModulus().toString(2).length();
			int blockSize = keySize / 8 - 11;
			byte[] data = new byte[blockSize];
			int len = -1;
			while ((len = in.read(data)) != -1) {
				out.write(cipher.doFinal(data, 0, len));
			}
			in.close();
			out.close();
		} catch (Exception e) {
			throw new RuntimeException("encrypt fail", e);
		}
	}

	public static DaoChongCertificate load(StoreData storeData) {
		return new DaoChongCertificate(storeData);
	}

	public static DaoChongCertificate load(String storePath, String storePass, String storeType) {
		return load(storePath, storePass, storeType, null);
	}

	public static DaoChongCertificate load(String storePath, String storePass, String storeType, String provider) {
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		data.setProvider(provider);
		DaoChongCertificate cert = new DaoChongCertificate(data);
		return cert;
	}

	public static X509Certificate loadCert(InputStream in) {
		return loadCert(in, null);
	}

	public static X509Certificate loadCert(InputStream in, String provider) {
		try {
			if (provider != null) {
				return (X509Certificate) CertificateFactory.getInstance("X509", provider).generateCertificate(in);
			} else {
				return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
			}
		} catch (Throwable e) {
			throw new RuntimeException("Load Certificate Fail", e);
		}
	}

	public static X509Certificate loadCert(String path) {
		return loadCert(path, null);
	}

	public static X509Certificate loadCert(String path, String provider) {
		try {
			return loadCert(new FileInputStream(path), provider);
		} catch (Throwable e) {
			throw new RuntimeException("Load Certificate Fail", e);
		}
	}

	public static DaoChongCertificate loadJKS(String storePath, String storePass) {
		return load(storePath, storePass, "JKS", null);
	}

	public static DaoChongCertificate loadJKS(String storePath, String storePass, String provider) {
		return load(storePath, storePass, "JKS", provider);
	}

	public static DaoChongCertificate loadP12(String storePath, String storePass) {
		return load(storePath, storePass, "PKCS12", null);
	}

	public static DaoChongCertificate loadP12(String storePath, String storePass, String provider) {
		return load(storePath, storePass, "PKCS12", provider);
	}

	public static byte[] sign(PrivateKey privateKey, byte[] data) {
		try {
			Signature signature = Signature.getInstance("SHA1WithRSA");
			signature.initSign(privateKey);
			signature.update(data);
			return signature.sign();
		} catch (Exception e) {
			throw new RuntimeException("sign fail", e);
		}
	}

	public static String sign(PrivateKey privateKey, File file) {
		try {
			String message = StringUtils.sha1(file);
			BigInteger bi = new BigInteger(1, sign(privateKey, message.getBytes()));
			return bi.toString(16);
		} catch (Exception e) {
			throw new RuntimeException("sign fail", e);
		}
	}

	public static boolean verify(Certificate cert, byte[] data, byte[] sign) {
		try {
			RSAPublicKey publicKey = (RSAPublicKey) cert.getPublicKey();
			Signature signature = Signature.getInstance("SHA1WithRSA");
			signature.initVerify(publicKey);
			signature.update(data);
			return signature.verify(sign);
		} catch (Exception e) {
			throw new RuntimeException("verify fail", e);
		}
	}

	public static boolean verify(Certificate cert, File file, String sign) {
		try {
			String message = StringUtils.sha1(file);
			return verify(cert, message.getBytes(), new BigInteger(sign, 16).toByteArray());
		} catch (Exception e) {
			throw new RuntimeException("verify fail", e);
		}
	}

	public static RSAPublicKey decodeSSHPublicKey(byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
		byte[] sshrsa = new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' };
		int start_index = sshrsa.length;
		/* Decode the public exponent */
		int len = new BigInteger(Arrays.copyOfRange(key, start_index, start_index + 4)).intValue();
		start_index += 4;
		byte[] pe_b = new byte[len];
		for (int i = 0; i < len; i++) {
			pe_b[i] = key[start_index++];
		}
		BigInteger pe = new BigInteger(pe_b);
		/* Decode the modulus */
		len = new BigInteger(Arrays.copyOfRange(key, start_index, start_index + 4)).intValue();
		start_index += 4;
		byte[] md_b = new byte[len];
		for (int i = 0; i < len; i++) {
			md_b[i] = key[start_index++];
		}
		BigInteger md = new BigInteger(md_b);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		KeySpec ks = new RSAPublicKeySpec(md, pe);
		return (RSAPublicKey) keyFactory.generatePublic(ks);
	}

	private StoreData storeData;
	private KeyStore store;
	private List<String> keys;
	private List<String> trusts;
	private List<String> all;

	private Map<String, Certificate> certs;

	private List<String> publicKeys;

	public DaoChongCertificate(StoreData storeData) {
		this.storeData = storeData;
		reload();
	}

	public boolean canIssue() {
		return canIssue(getDefaultPrivate());
	}

	public boolean canIssue(String alias) {
		if (this.isCARoot()) {
			return true;
		}
		// 获取
		return true;
	}

	public void changePassword(String old, String newPass) {
		if (this.storeData.getPassword().equals(old) && newPass != null && newPass.trim().length() > 0) {
			try {
				this.store.store(this.storeData.getOutputStream(), newPass.toCharArray());
				this.storeData.setPassword(newPass);
				reload();
			} catch (Throwable e) {
				throw new RuntimeException("Change Password Fail", e);
			}
		} else {
			throw new RuntimeException("Old Password validata Fail");
		}
	}

	private CertificateResult create(CertificateRequest request) {
		try {
			CertAndKeyGen gen = new CertAndKeyGen("RSA", "SHA1WithRSA");
			gen.generate(request.getLength());
			PublicKey publicKey = gen.getPublicKey();
			X509CertInfo info = new X509CertInfo();
			info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
			info.set(X509CertInfo.VALIDITY, new CertificateValidity(new Date(),
					new Date(System.currentTimeMillis() + request.getExpiredDays() * 86400L * 1000L)));
			info.set(X509CertInfo.SERIAL_NUMBER,
					new CertificateSerialNumber(new BigInteger(UUID.randomUUID().toString().replaceAll("-", ""), 16)));
			info.set(X509CertInfo.ISSUER, new CertificateIssuerName(new X500Name(request.toX500Name())));
			info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(new X500Name(request.toX500Name())));
			info.set(X509CertInfo.ALGORITHM_ID,
					new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid)));
			info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
			CertificateResult result = new CertificateResult();
			result.setRequest(request);
			result.setCertInfo(info);
			result.setPublicKey(publicKey);
			result.setPrivateKey(gen.getPrivateKey());
			return result;
		} catch (Throwable e) {
			throw new RuntimeException("Create Certificate Info Fail", e);
		}
	}

	public void createCARoot(CertificateRequest request) {
		CertificateResult result = create(request);
		try {
			X509CertInfo info = result.getCertInfo();
			CertInfoTemplate template = CertInfoTemplateFactory.get(CertInfoTemplateFactory.CAROOT);
			if (template != null) {
				CertificateExtensions exts = template.get(result, request);
				if (exts != null) {
					info.set(X509CertInfo.EXTENSIONS, exts);
				}
			}
			X509CertImpl cert = new X509CertImpl(info);
			cert.sign(result.getPrivateKey(), "SHA1WithRSA");
			this.store.setKeyEntry(request.getAlias(), result.getPrivateKey(),
					this.storeData.getPassword().toCharArray(), new Certificate[] { cert });
			save();
		} catch (Throwable e) {
			throw new RuntimeException("Create CA Certificate Fail", e);
		}
	}

	public void createSelf(CertificateRequest request) {
		CertificateResult result = create(request);
		try {
			X509CertImpl cert = new X509CertImpl(result.getCertInfo());
			cert.sign(result.getPrivateKey(), "SHA1WithRSA");
			this.store.setKeyEntry(request.getAlias(), result.getPrivateKey(),
					this.storeData.getPassword().toCharArray(), new Certificate[] { cert });
			save();
		} catch (Throwable e) {
			throw new RuntimeException("Create Self Certificate Fail", e);
		}
	}

	public byte[] decrypt(byte[] data) {
		return decrypt(getDefaultPrivate(), data);
	}

	public byte[] decrypt(String alias, byte[] data) {
		return decrypt(getPrivateKey(alias), data);
	}

	public void decrypt(InputStream in, OutputStream out) {
		decrypt(getDefaultPrivate(), in, out);
	}

	public void decrypt(String alias, InputStream in, OutputStream out) {
		decrypt(getPrivateKey(alias), in, out);
	}

	public byte[] encrypt(byte[] data) {
		return encrypt(getDefaultPrivate(), data);
	}

	public byte[] encrypt(String alias, byte[] data) {
		Certificate cert = this.getCertificate(alias);
		return encrypt(cert.getPublicKey(), data);
	}

	public void encrypt(InputStream in, OutputStream out) {
		encrypt(getDefaultPrivate(), in, out);
	}

	public void encrypt(String alias, InputStream in, OutputStream out) {
		Certificate cert = this.getCertificate(alias);
		encrypt(cert.getPublicKey(), in, out);
	}

	public void export(OutputStream out) {
		export(this.getDefaultPrivate(), out);
	}

	public void export(String certPath) {
		export(getDefaultPrivate(), certPath);
	}

	public void export(String alias, OutputStream out) {
		try {
			out.write(this.getCertificate(alias).getEncoded());
			out.close();
		} catch (Throwable e) {
			throw new RuntimeException("export Certificate '" + alias + "' Fail", e);
		}
	}

	public void export(String alias, String certPath) {
		try {
			export(alias, new FileOutputStream(certPath));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public void exportCrt(OutputStream out) {
		exportCrt(this.getDefaultPrivate(), out);
	}

	public void exportCrt(String certPath) {
		exportCrt(getDefaultPrivate(), certPath);
	}

	public void exportCrt(String alias, OutputStream out) {
		try {
			out.write("-----BEGIN CERTIFICATE-----".getBytes());
			out.write("\r\n".getBytes());
			out.write(new BASE64Encoder().encode(this.getCertificate(alias).getEncoded()).getBytes());
			out.write("\r\n".getBytes());
			out.write("-----END CERTIFICATE-----".getBytes());
			out.close();
		} catch (Throwable e) {
			throw new RuntimeException("export Certificate '" + alias + "' Fail", e);
		}
	}

	public void exportCrt(String alias, String certPath) {
		try {
			exportCrt(alias, new FileOutputStream(certPath));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public void exportKey(OutputStream out) {
		exportKey(this.getDefaultPrivate(), out);
	}

	public void exportKey(String certPath) {
		exportKey(getDefaultPrivate(), certPath);
	}

	public void exportKey(String alias, OutputStream out) {
		try {
			PrivateKey privateKey = this.getPrivateKey(alias);
			PKCS8EncodedKeySpec kpr = new PKCS8EncodedKeySpec(privateKey.getEncoded());
			out.write("-----BEGIN RSA PRIVATE KEY-----".getBytes());
			out.write("\r\n".getBytes());
			out.write(new BASE64Encoder().encode(kpr.getEncoded()).getBytes());
			out.write("\r\n".getBytes());
			out.write("-----END RSA PRIVATE KEY-----".getBytes());
			out.close();
		} catch (Throwable e) {
			throw new RuntimeException("export Certificate '" + alias + "' Fail", e);
		}
	}

	public void exportKey(String alias, String certPath) {
		try {
			exportKey(alias, new FileOutputStream(certPath));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public List<String> getAll() {
		return all;
	}

	private String getAutoAlias() {
		return BeanUtil.uuid();
	}

	public X509Certificate getCertificate() {
		return getCertificate(getDefaultPrivate());
	}

	public X509Certificate getCertificate(String alias) {
		try {
			return (X509Certificate) this.store.getCertificate(alias);
		} catch (Throwable e) {
			throw new RuntimeException("Get Certificate '" + alias + "' Fail", e);
		}
	}

	public Certificate[] getCertificateChain() {
		return getCertificateChain(getDefaultPrivate());
	}

	public Certificate[] getCertificateChain(String alias) {
		try {
			return this.store.getCertificateChain(alias);
		} catch (Throwable e) {
			throw new RuntimeException("Get Certificate '" + alias + "' Fail", e);
		}
	}

	public Map<String, Certificate> getCerts() {
		return certs;
	}

	public DaoChongCRL getCRL() {
		return new DaoChongCRL(this, getDefaultPrivate());
	}

	public DaoChongCRL getCRL(String rootAlias) {
		return new DaoChongCRL(this, rootAlias);
	}

	public DaoChongCRL getCRL(String rootAlias, int expiredDays) {
		return new DaoChongCRL(this, rootAlias, expiredDays);
	}

	public String getDefaultPrivate() {
		try {
			List<String> list = Collections.list(store.aliases());
			for (String alias : list) {
				if (store.isKeyEntry(alias)) {
					return alias;
				}
			}
			throw new RuntimeException("No Default Alias Fail");
		} catch (RuntimeException e) {
			throw e;
		} catch (Throwable e) {
			throw new RuntimeException("Get Default Alias Fail", e);
		}
	}

	public List<String> getKeys() {
		return keys;
	}

	public PrivateKey getPrivateKey() {
		return getPrivateKey(getDefaultPrivate());
	}

	public PrivateKey getPrivateKey(String alias) {
		try {
			return (PrivateKey) this.store.getKey(alias, this.storeData.getPassword().toCharArray());
		} catch (Throwable e) {
			throw new RuntimeException("Get Certificate '" + alias + "' PrivateKey Fail", e);
		}
	}

	public List<String> getTrusts() {
		return trusts;
	}

	public boolean isCARoot() {
		return isCARoot(getDefaultPrivate());
	}

	public boolean isCARoot(String alias) {
		X509Certificate cert = this.getCertificate(alias);
		return cert.getSubjectDN().equals(cert.getIssuerDN());
	}

	public boolean isCASub() {
		return isCASub(getDefaultPrivate());
	}

	public boolean isCASub(String alias) {
		X509Certificate cert = this.getCertificate(alias);
		if (cert.getSubjectDN().equals(cert.getIssuerDN())) {
			return false;
		}
		return canIssue(alias);
	}

	public DaoChongCertificate issue(CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueServer(getDefaultPrivate(), request);
		return save(result, storeData);
	}

	public CertificateResult issue(CertificateRequest request, String templateId) {
		return issue(getDefaultPrivate(), request, templateId);
	}

	public DaoChongCertificate issue(CertificateRequest request, String templateId, String storePath, String storePass,
			String storeType) {
		return issue(getDefaultPrivate(), request, templateId, storePath, storePass, storeType, null);
	}

	public DaoChongCertificate issue(CertificateRequest request, String templateId, String storePath, String storePass,
			String storeType, String provider) {
		return issue(getDefaultPrivate(), request, templateId, storePath, storePass, storeType, provider);
	}

	public DaoChongCertificate issue(String alias, CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueServer(alias, request);
		return save(result, storeData);
	}

	public CertificateResult issue(String rootAlias, CertificateRequest request, String templateId) {
		CertificateResult result = create(request);
		try {
			X509CertInfo info = result.getCertInfo();
			info.set(X509CertInfo.ISSUER,
					new CertificateIssuerName(new X500Name(this.getCertificate(rootAlias).getSubjectDN().getName())));
			info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(new X500Name(request.toX500Name())));
			CertInfoTemplate template = CertInfoTemplateFactory.get(templateId);
			if (template != null) {
				CertificateExtensions exts = template.get(result, request, this.getCertificate(rootAlias));
				if (exts != null) {
					info.set(X509CertInfo.EXTENSIONS, exts);
				}
			}
			X509CertImpl cert = new X509CertImpl(info);
			cert.sign((PrivateKey) this.store.getKey(rootAlias, this.storeData.getPassword().toCharArray()),
					"SHA1WithRSA");
			result.setCertificate(cert);
			Certificate[] temp = this.store.getCertificateChain(rootAlias);
			Certificate[] chain = new Certificate[temp.length + 1];
			chain[0] = cert;
			for (int i = 0; i < temp.length; i++) {
				chain[(i + 1)] = temp[i];
			}
			result.setCertificateChain(chain);
			return result;
		} catch (Throwable e) {
			throw new RuntimeException("ISSUE Certificate Fail", e);
		}
	}

	public DaoChongCertificate issue(String alias, CertificateRequest request, String templateId, String storePath,
			String storePass, String storeType, String provider) {
		CertificateResult result = issue(alias, request, templateId);
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		data.setProvider(provider);
		return save(result, data);
	}

	public CertificateResult issueCASub(CertificateRequest request) {
		return issueCASub(getDefaultPrivate(), request);
	}

	public DaoChongCertificate issueCASub(CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueCASub(getDefaultPrivate(), request);
		return save(result, storeData);
	}

	public DaoChongCertificate issueCASub(CertificateRequest request, String storePath, String storePass,
			String storeType) {
		return issueCASub(request, storePath, storePass, storeType, null);
	}

	public DaoChongCertificate issueCASub(CertificateRequest request, String storePath, String storePass,
			String storeType, String provider) {
		return issueCASub(getDefaultPrivate(), request, storePath, storePass, storeType, provider);
	}

	public CertificateResult issueCASub(String rootAlias, CertificateRequest request) {
		return issue(rootAlias, request, CertInfoTemplateFactory.CASUB);
	}

	public DaoChongCertificate issueCASub(String alias, CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueCASub(alias, request);
		return save(result, storeData);
	}

	public DaoChongCertificate issueCASub(String alias, CertificateRequest request, String storePath, String storePass,
			String storeType, String provider) {
		CertificateResult result = issueCASub(alias, request);
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		data.setProvider(provider);
		return save(result, data);
	}

	public CertificateResult issueServer(CertificateRequest request) {
		return issueServer(getDefaultPrivate(), request);
	}

	public DaoChongCertificate issueServer(CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueServer(getDefaultPrivate(), request);
		return save(result, storeData);
	}

	public DaoChongCertificate issueServer(CertificateRequest request, String storePath, String storePass,
			String storeType) {
		return issueServer(request, storePath, storePass, storeType, null);
	}

	public DaoChongCertificate issueServer(CertificateRequest request, String storePath, String storePass,
			String storeType, String provider) {
		return issueServer(getDefaultPrivate(), request, storePath, storePass, storeType, provider);
	}

	public CertificateResult issueServer(String rootAlias, CertificateRequest request) {
		return issue(rootAlias, request, CertInfoTemplateFactory.SERVER);
	}

	public DaoChongCertificate issueServer(String alias, CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueServer(alias, request);
		return save(result, storeData);
	}

	public DaoChongCertificate issueServer(String alias, CertificateRequest request, String storePath, String storePass,
			String storeType, String provider) {
		CertificateResult result = issueServer(alias, request);
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		data.setProvider(provider);
		return save(result, data);
	}

	public CertificateResult issueStand(CertificateRequest request) {
		return issueStand(getDefaultPrivate(), request);
	}

	public DaoChongCertificate issueStand(CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueStand(getDefaultPrivate(), request);
		return save(result, storeData);
	}

	public DaoChongCertificate issueStand(CertificateRequest request, String storePath, String storePass,
			String storeType) {
		return issueStand(request, storePath, storePass, storeType, null);
	}

	public DaoChongCertificate issueStand(CertificateRequest request, String storePath, String storePass,
			String storeType, String provider) {
		return issueStand(getDefaultPrivate(), request, storePath, storePass, storeType, provider);
	}

	public CertificateResult issueStand(String rootAlias, CertificateRequest request) {
		return issue(rootAlias, request, "");
	}

	public DaoChongCertificate issueStand(String alias, CertificateRequest request, StoreData storeData) {
		CertificateResult result = issueStand(alias, request);
		return save(result, storeData);
	}

	public DaoChongCertificate issueStand(String alias, CertificateRequest request, String storePath, String storePass,
			String storeType, String provider) {
		CertificateResult result = issueStand(alias, request);
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		data.setProvider(provider);
		return save(result, data);
	}

	public boolean isTrust(X509Certificate cert) {
		try {
			cert.checkValidity();
			String publicKey = StringUtils.hexEncode(new KeyIdentifier(cert.getPublicKey()).getIdentifier());
			if (this.publicKeys.contains(publicKey)) {
				return true;
			}
			X509CertInfo info = (X509CertInfo) (X509CertImpl.toImpl(cert))
					.get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
			CertificateExtensions ext = (CertificateExtensions) info.get(X509CertInfo.EXTENSIONS);
			for (Object et : ext.getAllExtensions()) {
				if (et instanceof AuthorityKeyIdentifierExtension) {
					AuthorityKeyIdentifierExtension ae = (AuthorityKeyIdentifierExtension) et;
					KeyIdentifier ki = (KeyIdentifier) ae.get(AuthorityKeyIdentifierExtension.KEY_ID);
					publicKey = StringUtils.hexEncode(ki.getIdentifier());
					return this.publicKeys.contains(publicKey);
				}
			}
		} catch (Exception e) {

		}
		return false;
	}

	public void reload() {
		try {
			this.all = new ArrayList<String>();
			this.certs = new LinkedHashMap<String, Certificate>();
			this.keys = new ArrayList<String>();
			this.trusts = new ArrayList<String>();
			this.keys = new ArrayList<String>();
			this.trusts = new ArrayList<String>();
			this.store = storeData.getKeyStore();
			this.publicKeys = new ArrayList<String>();
			this.all.addAll(Collections.list(this.store.aliases()));
			for (String alias : this.all) {
				if (this.store.isKeyEntry(alias)) {
					this.keys.add(alias);
				} else {
					this.trusts.add(alias);
				}
				Certificate cert = this.store.getCertificate(alias);
				this.certs.put(alias, cert);
				this.publicKeys.add(StringUtils.hexEncode(new KeyIdentifier(cert.getPublicKey()).getIdentifier()));
			}
		} catch (Throwable e) {
			throw new RuntimeException("load Store Fail", e);
		}
	}

	public X509Certificate renew(Certificate cert, int expiredDays) {
		return renew(getDefaultPrivate(), cert, expiredDays);
	}

	public void renew(StoreData data, int expiredDays) {
		renew(getDefaultPrivate(), data, expiredDays, null);
	}

	public void renew(StoreData data, String alias, int expiredDays) {
		renew(getDefaultPrivate(), data, expiredDays, alias);
	}

	public X509Certificate renew(String rootAlias, Certificate cert, int expiredDays) {
		try {
			if (!((X509Certificate) cert).getIssuerDN().getName()
					.equals(this.getCertificate(rootAlias).getSubjectDN().getName())) {
				throw new RuntimeException("The Certificate is not bee ISSUE by this");
			}
			X509CertImpl x509 = (X509CertImpl) cert;
			X509CertInfo info = (X509CertInfo) x509.get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
			info.set(X509CertInfo.ISSUER,
					new CertificateIssuerName(new X500Name(this.getCertificate(rootAlias).getSubjectDN().getName())));
			info.set(X509CertInfo.SUBJECT,
					new CertificateSubjectName(new X500Name(((X509Certificate) cert).getSubjectDN().getName())));
			info.set(X509CertInfo.VALIDITY, new CertificateValidity(new Date(),
					new Date(System.currentTimeMillis() + expiredDays * 86400L * 1000L)));
			X509CertImpl newCert = new X509CertImpl(info);
			newCert.sign((PrivateKey) this.store.getKey(rootAlias, this.storeData.getPassword().toCharArray()),
					"SHA1WithRSA");
			return newCert;
		} catch (Throwable e) {
			throw new RuntimeException("RENEW Certificate Fail", e);
		}
	}

	public void renew(String rootAlias, StoreData data, int expiredDays, String alias) {
		DaoChongCertificate store = new DaoChongCertificate(data);
		if (alias == null) {
			alias = store.getDefaultPrivate();
		}
		X509Certificate cert = renew(rootAlias, store.getCertificate(alias), expiredDays);
		try {
			store.store.setKeyEntry(alias, store.getPrivateKey(alias), data.getPassword().toCharArray(),
					new Certificate[] { cert });
			store.save();
		} catch (Throwable e) {
			throw new RuntimeException("RENEW Certificate Fail", e);
		}
	}

	public void save(OutputStream out) {
		try {
			if (out != null) {
				this.store.store(out, this.storeData.getPassword().toCharArray());
				reload();
			}
		} catch (Throwable e) {
			throw new RuntimeException("load Store Fail", e);
		}
	}

	public void save() {
		try {
			OutputStream out = this.storeData.getOutputStream();
			if (out != null) {
				this.store.store(out, this.storeData.getPassword().toCharArray());
				reload();
			}
		} catch (Throwable e) {
			throw new RuntimeException("load Store Fail", e);
		}
	}

	public DaoChongCertificate save(CertificateResult result, StoreData storeData) {
		try {
			String provider = storeData.getProvider();
			KeyStore store;
			if (provider != null) {
				store = KeyStore.getInstance(storeData.getStoreType(), provider);
			} else {
				store = KeyStore.getInstance(storeData.getStoreType());
			}
			store.load(null, storeData.getPassword().toCharArray());
			store.setKeyEntry(result.getRequest().getAlias(), result.getPrivateKey(),
					storeData.getPassword().toCharArray(), result.getCertificateChain());
			for (int i = 1; i < result.getCertificateChain().length; i++) {
				store.setCertificateEntry(getAutoAlias(), result.getCertificateChain()[i]);
			}
			store.store(storeData.getOutputStream(), storeData.getPassword().toCharArray());
			return DaoChongCertificate.load(storeData);
		} catch (Throwable e) {
			throw new RuntimeException("Save Certificate Fail", e);
		}
	}

	public byte[] sign(byte[] data) {
		return sign(getDefaultPrivate(), data);
	}

	public String sign(File file) {
		return sign(getDefaultPrivate(), file);
	}

	public byte[] sign(String alias, byte[] data) {
		RSAPrivateKey privateKey = (RSAPrivateKey) this.getPrivateKey(alias);
		return sign(privateKey, data);
	}

	public String sign(String alias, File file) {
		RSAPrivateKey privateKey = (RSAPrivateKey) this.getPrivateKey(alias);
		return sign(privateKey, file);
	}

	public DaoChongCertificate translateTo(StoreData data) {
		DaoChongCertificate re = new DaoChongCertificate(data);
		try {
			boolean support = true;
			for (String alias : Collections.list(this.store.aliases())) {
				if (this.store.isKeyEntry(alias)) {
					re.store.setKeyEntry(alias, this.store.getKey(alias, this.storeData.getPassword().toCharArray()),
							data.getPassword().toCharArray(), this.store.getCertificateChain(alias));
				} else if (support) {
					try {
						re.store.setCertificateEntry(alias, this.store.getCertificate(alias));
					} catch (Throwable e) {
						support = false;
					}
				}
			}
			re.save();
		} catch (Throwable e) {
			throw new RuntimeException("Translate Certificate Fail", e);
		}
		return re;
	}

	public DaoChongCertificate translateTo(String storePath, String storePass, String storeType) {
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		return translateTo(data);
	}

	public DaoChongCertificate translateTo(String storePath, String storePass, String storeType, String provider) {
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType(storeType);
		data.setProvider(provider);
		return translateTo(data);
	}

	public DaoChongCertificate translateToJKS(String storePath, String storePass) {
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType("JKS");
		return translateTo(data);
	}

	public DaoChongCertificate translateToP12(String storePath, String storePass) {
		StoreData data = new StoreData();
		data.setFilePath(storePath);
		data.setPassword(storePass);
		data.setStoreType("PKCS12");
		return translateTo(data);
	}

	public void translateToPem(String storePath) {
		this.exportKey(storePath);
	}

	public void translateToPem(String alias, String storePath) {
		this.exportKey(alias, storePath);
	}

	public void translateToOpenSSH(String storePath, String username, String domain) {
		translateToOpenSSH(getDefaultPrivate(), storePath, username, domain, this.storeData.getPassword());
	}

	public void translateToOpenSSH(String alias, String storePath, String username, String domain) {
		translateToOpenSSH(alias, storePath, username, domain, this.storeData.getPassword());
	}

	public void translateToOpenSSH(String alias, String storePath, String username, String domain, String password) {
		try {
			translateToOpenSSH(alias, username, domain, password,
					new FileOutputStream(storePath + "/" + username + "_rsa.pub"),
					new FileOutputStream(storePath + "/" + username + "_rsa"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void translateToOpenSSH(String alias, String username, String domain, String password, OutputStream pub,
			OutputStream pri) {
		try {
			Certificate cert = this.store.getCertificate(alias);
			RSAPublicKey publicKey = (RSAPublicKey) cert.getPublicKey();
			SSHCertificateUtils.writePub(pub, username, domain, publicKey);
			RSAPrivateKey privateKey = (RSAPrivateKey) this.getPrivateKey(alias);
			SSHCertificateUtils.writePrivate(pri, privateKey, password);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void trust(Certificate cert) {
		trust(cert, getAutoAlias());
	}

	public void trust(Certificate cert, String alias) {
		try {
			this.store.setCertificateEntry(alias, cert);
		} catch (Throwable e) {
			throw new RuntimeException("Trust Certificate Fail ", e);
		}
	}

	public void trust(InputStream in) {
		trust(in, getAutoAlias());
	}

	public void trust(InputStream in, String alias) {
		try {
			trust(CertificateFactory.getInstance("X509").generateCertificate(in), alias);
		} catch (Throwable e) {
			throw new RuntimeException("Trust Certificate Fail ", e);
		}
	}

	public void trust(String path) {
		trust(path, getAutoAlias());
	}

	public void trust(String path, String alias) {
		try {
			trust(new FileInputStream(path), alias);
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	public void trusts(List<Certificate> certs) {
		try {
			for (Certificate cert : certs) {
				this.store.setCertificateEntry(getAutoAlias(), cert);
			}
			save();
		} catch (Throwable e) {
			throw new RuntimeException("Trust Certificate Fail ", e);
		}
	}

	public void trusts(Map<String, Certificate> certs) {
		try {
			for (String alias : certs.keySet()) {
				this.store.setCertificateEntry(alias, certs.get(alias));
			}
			save();
		} catch (Throwable e) {
			throw new RuntimeException("Trust Certificate Fail ", e);
		}
	}

	public boolean verify(byte[] data, byte[] sign) {
		return verify(getDefaultPrivate(), data, sign);
	}

	public boolean verify(File file, String sign) {
		return verify(getDefaultPrivate(), file, sign);
	}

	public boolean verify(String alias, byte[] data, byte[] sign) {
		Certificate cert = this.getCertificate(alias);
		return verify(cert, data, sign);
	}

	public boolean verify(String alias, File file, String sign) {
		Certificate cert = this.getCertificate(alias);
		return verify(cert, file, sign);
	}
}
